package com.google.inject.internal; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.Provider; import com.google.inject.ProvisionException; import ru.vyarus.guice.ext.core.generator.DynamicClassGenerator; import ru.vyarus.guice.ext.core.generator.anchor.AnchorBean; import javax.inject.Inject; import javax.inject.Singleton; import java.lang.annotation.Annotation; /** * Provider allows using interfaces or abstract classes as normal guice beans. * Simply annotate interface or abstract class with {@code @ProvidedBy(DynamicClassProvider.class)} * and use it as usual guice bean. * <p>Original bean will be correctly handled by guice aop: provider will generate new class, which guice could * use for proxy generation.</p> * <p>If used with injectors hierarchy or within private modules, use together with * {@link ru.vyarus.guice.ext.core.generator.anchor.GeneratorAnchorModule} to properly scope dynamic bindings.</p> * <p>Providers use guice package to use internal guice api (to resolve actual required type)</p> * * @author Vyacheslav Rusakov * @see ru.vyarus.guice.ext.core.generator.DynamicClassGenerator if you prefer direct registration in module * @since 07.12.2014 */ @Singleton public class DynamicClassProvider implements Provider<Object> { private final Injector injector; @Inject public DynamicClassProvider(final Injector injector) { this.injector = injector; } @Override @SuppressWarnings({"PMD.PreserveStackTrace", "PMD.NullAssignment"}) public Object get() { try { return ((InjectorImpl) injector).callInContext(new ContextualCallable<Object>() { @Override public Object call(final InternalContext context) { // check if (possibly) child context contains anchor bean definition final boolean hasAnchor = injector.getExistingBinding(Key.get(AnchorBean.class)) != null; final Class<?> abstractType = context.getDependency().getKey().getTypeLiteral().getRawType(); final Class<?> generatedType = DynamicClassGenerator.generate(abstractType, getScopeAnnotation(), hasAnchor ? AnchorBean.class : null); return injector.getInstance(generatedType); } }); } catch (ErrorsException e) { throw new ProvisionException(e.getErrors().getMessages()); } } /** * Override it to specify different annotation. By default, no annotation specified which will implicitly lead * to default prototype scope. * * @return scope annotation which should be applied to generated class */ protected Class<? extends Annotation> getScopeAnnotation() { return null; } }